﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using SoftwareConsulting.BI11.Studio.Core.Project.BIML.Actors;
using SoftwareConsulting.BI11.Studio.Core.Project.BIML.Semantics;
using SoftwareConsulting.BI11.Studio.Core.Project.Metadata;
using SoftwareConsulting.BI11.Studio.ProcessWait;
using SoftwareConsulting.BI11.Studio.UI.Properties;
using SoftwareConsulting.BI11.Studio.InternalServices;

namespace SoftwareConsulting.BI11.Studio.UI.IDE
{
    /// <summary>
    /// Класс формы свода действий
    /// </summary>
    public partial class ActionSetForm : Form
    {
        private IDEForm _ide; //ссылка на форму среды разработки
        private IActionSet _actset; //редактируемый свод действий        
        private IIntellisense _intellisense; //интеллисенс        
        private ContextMenuStrip _intellisenceWindow; //окно интеллисенса
        private bool _programOpenedIntellisence; //флаг программного раскрытия окна интеллисенса
        private bool NoModified; //флаг необработки изменений кода
        private int _lastSelectedIndex; //индекс последнего выбранного идентификатора в интеллисенсе        

        /// <summary>
        /// Возвращает редактируемый свод действий
        /// </summary>
        public IActionSet ActionSet
        {
            get
            {
                return _actset;
            }
        }

        /// <summary>
        /// Убирает окно интеллисенса
        /// </summary>
        private void HideIntellisence()
        {
            _intellisenceWindow.Hide();            
            CodeBox.Focus();            
        }

        /// <summary>
        /// Выполняет вставку строки из интеллисенса
        /// </summary>
        private void ExecuteIntellisence(ContextMenuStrip contextMenu)
        {
            ToolStripItemCollection items = contextMenu.Items;
            string seltext = string.Empty;
            foreach(ToolStripMenuItem ti in items)            
            {
                if (!string.IsNullOrEmpty(seltext))
                    break;                
                if (ti.Selected)
                {
                    if (!ti.Enabled)
                    {
                        return;
                    }
                    seltext = ti.Text;
                    _lastSelectedIndex = contextMenu.Items.IndexOf(ti);                    
                }
            }            
            int index = CodeBox.SelectionStart;
            int rowindex = CodeBox.GetLineFromCharIndex(index);
            int indexend = CodeBox.SelectionStart + CodeBox.SelectionLength;
            int rowindexend = CodeBox.GetLineFromCharIndex(indexend);
            if (rowindex == rowindexend)
            {
                int colindex = 0;
                int colindexend = 0;
                string s = string.Empty;
                if (CodeBox.Lines.Count() > 0)
                {
                    s = CodeBox.Lines[rowindex];
                    colindex = index - CodeBox.GetFirstCharIndexFromLine(rowindex);
                    colindexend = indexend - CodeBox.GetFirstCharIndexFromLine(rowindexend);
                }
                s = s.Substring(0, colindex) + seltext + s.Substring(colindexend);
                //а далее сплошное извращенство лишь потому
                //что напрямую сказать Lines[0]="ABC" нельзя
                //(не срабатывает)
                string bigs = CodeBox.Text;
                bigs = bigs.Substring(0, CodeBox.GetFirstCharIndexFromLine(rowindex)) + s;
                if (rowindex < (CodeBox.Lines.Count() - 1))
                {
                    bigs = bigs + CodeBox.Text.Substring(CodeBox.GetFirstCharIndexFromLine(rowindex + 1));
                }
                int selneed = CodeBox.SelectionStart + seltext.Length;
                CodeBox.Text = bigs;
                CodeBox.SelectionStart = selneed;
            }
            HideIntellisence();            
        }

        /// <summary>
        /// Обработка раскрытия контекстного меню
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _intellisenceWindow_Opening(object sender, CancelEventArgs e)
        {
            if (!_programOpenedIntellisence)
            {
                CodeBox.SelectionStart = CodeBox.GetCharIndexFromPosition(CodeBox.PointToClient(new Point(MousePosition.X, MousePosition.Y)));
                CodeBox.SelectionLength = 0;
            }
            _programOpenedIntellisence = false;
            PrepareIntellisense();
        }

        /// <summary>
        /// Обработчика клика по пункту интеллисенса
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _intellisenceWindow_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            ExecuteIntellisence(sender as ContextMenuStrip);
        }

        /// <summary>
        /// Обработчик нажатия клавиши внутри окна интеллисенса
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _intellisenceListView_KeyDown(object sender, KeyEventArgs e)
        {
            switch (e.KeyCode)
            {                                
                case Keys.Space:                     
                    ExecuteIntellisence(sender as ContextMenuStrip);
                    break;
            }            
        }
        
        /// <summary>
        /// Инициализация формы
        /// </summary>
        public ActionSetForm(IDEForm IDE, IActionSet actionSet)
        {            
            _ide = IDE;
            _actset = actionSet;
            InitializeComponent();
            NoModified = true;
            Code = _actset.Lines;
            NoModified = false;
        }

        /// <summary>
        /// Возвращает/устанавливает код модуля
        /// </summary>
        public List<string> Code
        {
            get
            {
                List<string> lines = new List<string>();
                lines.AddRange(CodeBox.Lines);
                return lines;
            }
            set
            {
                CodeBox.Lines = value.ToArray();
            }
        }

        /// <summary>
        /// Обновляет название окна
        /// </summary>
        public void UpdateCaptionName()
        {
            Text = Resources.BI_IDEWINDOW_ACTIONSET_CAPTION + " - [" + _actset.Name + "]";
        }        

        /// <summary>
        /// Инициализирует форму
        /// </summary>
        private void Initialize()
        {
            UpdateCaptionName();
            _ide.UpdateAbilities(null);
            _intellisense = new Intellisense(_ide.ProjectLink);
            _intellisense.CreateFictiveScope();
            UpdateIntellisence();
            _intellisenceWindow = new ContextMenuStrip();
            _intellisenceWindow.ShowImageMargin = false;
            _intellisenceWindow.Opening += new CancelEventHandler(_intellisenceWindow_Opening);
            _intellisenceWindow.KeyDown += new KeyEventHandler(_intellisenceListView_KeyDown);
            _intellisenceWindow.ItemClicked += new ToolStripItemClickedEventHandler(_intellisenceWindow_ItemClicked);
            _programOpenedIntellisence = false;
            CodeBox.ContextMenuStrip = _intellisenceWindow;
            //_intellisenceWindow.Visible = true;
            _lastSelectedIndex = 0;            
        }

        /// <summary>
        /// Обновляет интеллисенс
        /// </summary>
        private void UpdateIntellisence()
        {            
            _intellisense.BuildIdentsTableInStandaloneThread(Code);
        }
        
        /// <summary>
        /// Ставит курсор в заданное место редактора
        /// </summary>
        /// <param name="row"></param>
        /// <param name="column"></param>
        public void PutCursor(int row, int column)
        {
            CodeBox.SelectionStart = CodeBox.GetFirstCharIndexFromLine(row) + column;
            CodeBox.SelectionLength = 0;
            CodeBox.ScrollToCaret();
        }

        /// <summary>
        /// Подготовка интеллисенса
        /// </summary>
        private void PrepareIntellisense()
        {
            int line = CodeBox.GetLineFromCharIndex(CodeBox.SelectionStart);
            List<string> idents = _intellisense.GetAppropriateIdents(line, CodeBox.SelectionStart - CodeBox.GetFirstCharIndexFromLine(line));
            if (idents == null)
            {
                //уже до места вызова найдена ошибка в коде,
                //интеллисенс не может быть отображен
                idents = new List<string>();
                idents.Add(Resources.BI_BIML_INTELLISENSE_ERRORFOUND);
            }
            else
            {
                idents.Sort();
            }
            //вычищаем старое
            ToolStripItemCollection items = _intellisenceWindow.Items;
            items.Clear();
            //добавляем новое            
            for (int i = 0; i < idents.Count; i++)
            {
                items.Add(idents[i]);
                if (idents[i] == Resources.BI_BIML_INTELLISENSE_ERRORFOUND)
                {
                    items[i].Enabled = false;
                }
            }            
            if (items.Count > _lastSelectedIndex)
            {
                ToolStripMenuItem tis = items[_lastSelectedIndex] as ToolStripMenuItem;
                tis.Select();
            }
        }

        /// <summary>
        /// Отображает окно интеллисенса
        /// </summary>
        private void ShowIntellisense(Point point)
        {
            //PrepareIntellisense();
            _programOpenedIntellisence = true;
            _intellisenceWindow.Show(CodeBox, point);
        }

        /// <summary>
        /// Компилирует свод действий
        /// </summary>
        public void Compile()
        {
            Processing.BeginWait();
            Processing.SetDescription(Resources.BI_IDEWINDOW_COMPILING);
            Save();
            _ide.Errors = _actset.Boost();
            _ide.ShowErrors();
            Processing.EndWait();
        }

        /// <summary>
        /// Сохраняет свод действий
        /// </summary>
        public void Save()
        {
            if (_actset.Modified)
            {
                _actset.Lines = Code;
                DialogResult toSaveProjectToo = DialogResult.Yes;
                if (_ide.ProjectLink.Modified)
                {
                    toSaveProjectToo = GeneralMethods.ShowWarning(Resources.BI_IDEWINDOW_ACTIONSET_PROJECTMODIFIED, MessageBoxButtons.YesNo);
                }
                _actset.ApplyChanges();
                if (toSaveProjectToo == DialogResult.Yes)
                {
                    _ide.ProjectExplorerLink.SaveProject();
                }
                _ide.UpdateAbilities(null);
            }
        }

        /// <summary>
        /// Обрабатывает активацию формы
        /// </summary>
        private void ProcessActivation()
        {
            _ide.RefreshUsed();
            _ide.UpdateAbilities(null);
        }

        /// <summary>
        /// Проверка на возможность закрытия свода действий
        /// </summary>
        /// <returns>true - закрывать можно; false - нельзя</returns>
        private bool AskBeforeClosing()
        {
            _ide.Focus();
            this.Focus();
            bool toContinue = !_actset.Modified;
            if (!toContinue)
            {
                DialogResult d = GeneralMethods.ShowWarning(Resources.BI_IDEWINDOW_ACTIONSET_MODIFIED, MessageBoxButtons.YesNoCancel);
                switch (d)
                {
                    case DialogResult.Yes:
                        Save();
                        toContinue = true;
                        break;
                    case DialogResult.No:
                        toContinue = true;
                        break;
                }
            }            
            return toContinue;
        }

        /// <summary>
        /// Обрабатываем изменение кода
        /// </summary>
        private void ProcessTextChange()
        {
            if (!NoModified)
            {
                bool before = _actset.Modified;
                _actset.Modified = true;
                if (!before)
                    _ide.UpdateAbilities(null);
            }
        }

        private void компилироватьToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Compile();
        }

        private void BIMLModuleForm_Load(object sender, EventArgs e)
        {
            Initialize();
        }

        private void BIMLModuleForm_FormClosing(object sender, FormClosingEventArgs e)
        {            
            e.Cancel = !AskBeforeClosing();
            if (!e.Cancel)
            {
                _ide.UpdateAbilities(this);
            }
        }

        private void CodeBox_TextChanged(object sender, EventArgs e)
        {
            ProcessTextChange();            
        }

        private void CodeBox_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Control && (e.KeyCode == Keys.Space))
            {
                Point p = CodeBox.GetPositionFromCharIndex(CodeBox.SelectionStart);
                p.Offset(0, CodeBox.Font.Height);
                ShowIntellisense(p);
                //e.Handled = true;                                
            }
        }

        private void CodeBox_Enter(object sender, EventArgs e)
        {
            if (_intellisenceWindow.Visible)
            {
                HideIntellisence();
            }
        }

        private void CodeBox_KeyUp(object sender, KeyEventArgs e)
        {
            UpdateIntellisence();
        }

        private void ActionSetForm_Activated(object sender, EventArgs e)
        {
            ProcessActivation();
        }

    }
}